package goavro

import (
	"encoding/binary"
	"fmt"
	"io"
)

// rabinEmpty is a constant used to initialize the crc64Table, and to compute
// the CRC-64-AVRO fingerprint of every object schema.
const rabinEmpty = uint64(0xc15d213aa4d7a795)

// rabinTable is never modified after initialization but its values are read to
// compute the CRC-64-AVRO fingerprint of every schema its given.
var rabinTable = [256]uint64{
	0,
	3238593523956797946,
	6477187047913595892,
	8435907220062204430,
	12954374095827191784,
	11472609148414072338,
	16871814440124408860,
	14327483619285186022,
	16515860097293205755,
	14539261057490653441,
	13607494391182877455,
	10387063993012335349,
	6265406319754774291,
	8791864835633305321,
	1085550678754862311,
	2585467722461443357,
	5247393906202824413,
	7215812591205457703,
	1239030555549527337,
	4449591751341063379,
	18092457712352332085,
	15556728100436498639,
	11742789833002527425,
	10234164645493242683,
	12530812639509548582,
	9302088354573213660,
	17583729671266610642,
	15633189885995973672,
	2171101357509724622,
	3661574416647526452,
	5170935444922886714,
	7724537325157989312,
	10494787812405648826,
	13642865964979244096,
	14431625182410915406,
	16480541316673728436,
	2478061111099054674,
	1049933365183482792,
	8899183502682126758,
	6300970840149272668,
	8399466921467862337,
	6368420890995002555,
	3275086581351513781,
	108854135608684367,
	14364169659802000041,
	16980263386864569171,
	11435870349096892765,
	12845837170396948647,
	15669858317114364775,
	17692196227407282845,
	9265331945857609875,
	12422293323479818601,
	7688114635962061967,
	5062151678603773301,
	3698085083440658299,
	2279937883717887617,
	4342202715019449244,
	1203395666939462246,
	7323148833295052904,
	5282940851558637970,
	10341870889845773428,
	11778178981837571470,
	15449074650315978624,
	18057156506771531386,
	11669866394404287583,
	10160817855121008037,
	17874829710049597355,
	15339802717267265105,
	1311848476550706103,
	4523114428088083021,
	5464845951130112067,
	7432843562972398009,
	4956122222198109348,
	7509300761534850398,
	2099866730366965584,
	3591042414950500010,
	17798367005364253516,
	15848531969535615670,
	12601941680298545336,
	9372796311334617410,
	16798933842935724674,
	14253900473960229752,
	12736841781990005110,
	11255500115345754252,
	6550173162703027562,
	8509314479008689296,
	217708271217368734,
	3455596968422674276,
	870833084869474937,
	2370047569572014979,
	6194214610827729293,
	8721096401170761847,
	13822387873690697105,
	10602378625989962859,
	16587157392570359397,
	14609853536892473247,
	3483332339477899749,
	2064482512161650719,
	7616958077116566033,
	4991418462803860459,
	9480190278288059917,
	12637572737790640119,
	15741190762473065977,
	17762823925471730691,
	15376229271924123934,
	17983608511393921252,
	10124303357207546602,
	11561034798826117904,
	7396170166881316598,
	5356383260452470540,
	4559875767435775234,
	1420363961462201592,
	8684405430038898488,
	6085769495188764354,
	2406791333878924492,
	979366144819647798,
	14646297666590105808,
	16695918618875998506,
	10565881703117275940,
	13713538703073841886,
	11362911691697612739,
	12772455230081578553,
	14146576876296094775,
	16763373153642681805,
	3347869283551649835,
	182341662412566993,
	8616954185191982047,
	6585487012709290533,
	13933329357911598997,
	17126321439046432367,
	11006435164953838689,
	12992741788688209307,
	8257930048646602877,
	6803747195591438727,
	3132703159877387145,
	542775339377431155,
	2623696953101412206,
	619515277774763668,
	9046228856176166042,
	5871394916501263712,
	10929691902260224134,
	13501751302614184316,
	14865687125944796018,
	16338017159720129160,
	9912244444396218696,
	11925134239902742706,
	15018601523069700796,
	18202706530865158982,
	4199733460733931168,
	1637543290675756890,
	7182084829901000020,
	5717935174548446382,
	7834929158557182387,
	4632665972928804937,
	3844057317981030983,
	1849042541720329149,
	16103865201353027163,
	17549867708331900833,
	9700748483321744815,
	12280807109898935381,
	5834933197202143791,
	8937414855024798677,
	655924238275353051,
	2732422975565056033,
	16374796089197559239,
	14974255385173568573,
	13465025131935292979,
	10821211621719183305,
	13100346325406055124,
	11041713811386575662,
	17018628958017378592,
	13897997918303815898,
	435416542434737468,
	3097107305413864646,
	6911193936845348552,
	8293578696285179698,
	1741666169738949874,
	3808479038558283016,
	4740095139144029958,
	7870595381236532988,
	12388429221655458586,
	9736009554713699040,
	17442192802341523694,
	16068516186704462100,
	18239503069743100937,
	15127152172900050419,
	11888425678624364541,
	9803746554456753671,
	5681455845848806369,
	7073288438148047387,
	1673934641775824917,
	4308477092595991023,
	6966664678955799498,
	5503217582476919344,
	4128965024323301438,
	1566351579938693572,
	15233916154233132066,
	18417600011429070296,
	9982836925607720918,
	11996431537128302124,
	9627165335515697969,
	12207926510359495371,
	15886756170769674437,
	17332335396841578815,
	3917464579278591193,
	1922028658990515491,
	8051932600676513581,
	4850374241660872407,
	2917466598601071895,
	327962119137676525,
	8187398044598779619,
	6732512565967646489,
	11221777246008269567,
	13207379120439233285,
	14004037317153847563,
	17197450482186430705,
	14792340333762633196,
	16265093719173729302,
	10712766520904941080,
	13284123302255603682,
	9119751534871550468,
	5944212839312182270,
	2840727922924403184,
	836967320887912458,
	17368810860077796976,
	15995557527495450506,
	12171538990377528708,
	9518416773021940862,
	4813582667757848984,
	7943378085384837218,
	1958732289639295596,
	4025966300338256790,
	1458733299300535947,
	4093699022299389809,
	5610888623004134783,
	7002018658576923781,
	12103802978479819107,
	10018419036150929561,
	18310175810188503703,
	15198246066092718957,
	13391477134206599341,
	10748366240846565719,
	16157651908532642649,
	14756687855020634787,
	729366649650267973,
	2805444311502067391,
	6051901489239909553,
	9155087905094251851,
	6695738567103299670,
	8078825954266321324,
	364683324825133986,
	3025950744619954776,
	17233908370383964094,
	14112856248920397380,
	13170974025418581066,
	11113046258555286960,
}

// rabin returns an unsigned 64-bit integer Rabin fingerprint for buf.  NOTE:
// This is only used during Codec instantiation to calculate the Rabin
// fingerprint of the canonical schema.
func rabin(buf []byte) uint64 {
	fp := rabinEmpty
	for i := 0; i < len(buf); i++ {
		fp = (fp >> 8) ^ rabinTable[(byte(fp)^buf[i])&0xff] // unsigned right shift >>>
	}
	return fp
}

const soeMagicPrefix = 2                // 2-byte prefix for SOE encoded data
const soeHeaderLen = soeMagicPrefix + 8 // 2-byte prefix plus 8-byte fingerprint

// FingerprintFromSOE returns the unsigned 64-bit Rabin fingerprint from the
// header of a buffer that encodes a Single-Object Encoded datum.  This function
// is designed to be used to lookup a Codec that can decode the contents of the
// buffer.  Once a Codec is found that has the matching Rabin fingerprint, its
// NativeFromBinary method may be used to decode the remaining bytes returned as
// the second return value.  On failure this function returns an
// ErrNotSingleObjectEncoded error.
//
//	func decode(codex map[uint64]*goavro.Codec, buf []byte) error {
//	    // Perform a sanity check on the buffer, then return the Rabin fingerprint
//	    // of the schema used to encode the data.
//	    fingerprint, newBuf, err := goavro.FingerprintFromSOE(buf)
//	    if err != nil {
//	        return err
//	    }
//
//	    // Get a previously stored Codec from the codex map.
//	    codec, ok := codex[fingerprint]
//	    if !ok {
//	        return fmt.Errorf("unknown codec: %#x", fingerprint)
//	    }
//
//	    // Use the fetched Codec to decode the buffer as a SOE.
//	    //
//	    // Faster because SOE magic prefix and schema fingerprint already
//	    // checked and used to fetch the Codec.  Just need to decode the binary
//	    // bytes remaining after the prefix were removed.
//	    datum, _, err := codec.NativeFromBinary(newBuf)
//	    if err != nil {
//	        return err
//	    }
//
//	    _, err = fmt.Println(datum)
//	    return err
//	}
func FingerprintFromSOE(buf []byte) (uint64, []byte, error) {
	if len(buf) < soeHeaderLen {
		// Not enough bytes to encode schema fingerprint.
		return 0, nil, ErrNotSingleObjectEncoded(io.ErrShortBuffer.Error())
	}

	if buf[0] != 0xC3 || buf[1] != 0x01 {
		// Currently only one SOE prefix is recognized.
		return 0, nil, ErrNotSingleObjectEncoded(fmt.Sprintf("unknown SOE prefix: %#x", buf[:soeMagicPrefix]))
	}

	// Only recognizes single-object encodings format version 1.
	return binary.LittleEndian.Uint64(buf[soeMagicPrefix:]), buf[soeHeaderLen:], nil
}
